﻿using Quartz;
using Quartz.Impl;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HsServerHa.Common
{
    public class QuartzHelper
    {
        private static QuartzHelper instance = new QuartzHelper();

        public static QuartzHelper Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new QuartzHelper();
                }
                return instance;
            }
        }

        private const string JOB_GROUP_NAME = "JOB_GROUP_NAME";
        private const string TRIGGER_GROUP_NAME = "TRIGGER_GROUP_NAME";

        /// <summary>
        /// 程序调度
        /// </summary>
        private static IScheduler _scheduler;

        /// <summary>
        /// 初始化任务调度对象
        /// </summary>
        private QuartzHelper()
        {

        }


        /// <summary>
        /// 时间间隔执行任务
        /// </summary>
        /// <typeparam name="T">任务类，必须实现IJob接口</typeparam>
        /// <param name="seconds">时间间隔(单位：毫秒)</param>
        public async Task StartJobExecuteInterval<T>(int seconds, string jobName = null, string jobGroupName = null, string triggerName = null, string triggerGroupName = null) where T : IJob
        {
            await InitJobs();

            //IJobDetail job = JobBuilder.Create<T>().WithIdentity("job1", "group1").Build();
            IJobDetail job = JobBuilder.Create<T>().Build();

            ITrigger trigger = TriggerBuilder.Create()
                .StartNow()
                .WithSimpleSchedule(x => x.WithIntervalInSeconds(seconds).RepeatForever())
                .Build();

            await _scheduler.ScheduleJob(job, trigger);

            await _scheduler.Start();
        }

        /// <summary>
        /// 指定时间执行任务
        /// </summary>
        /// <typeparam name="T">任务类，必须实现IJob接口</typeparam>
        /// <param name="cronExpression">cron表达式，即指定时间点的表达式</param>
        public async Task<TriggerState> StartJobExecuteByCron<T>(string cronExpression, string jobName, object para = null, DateTime? startTime = null, DateTime? endTime = null, string jobGroupName = JOB_GROUP_NAME, string triggerName = null, string triggerGroupName = TRIGGER_GROUP_NAME) where T : IJob
        {
            await InitJobs();

            IJobDetail job = JobBuilder.Create<T>()
                    .WithIdentity(jobName, jobGroupName)
                    .Build();
            job.JobDataMap.Put("Runner", para);


            ITrigger trigger;
            if (startTime == null && endTime == null)
            {
                trigger = TriggerBuilder.Create()
                   .WithIdentity(triggerName ?? jobName, triggerGroupName)
                   .WithCronSchedule(cronExpression)
                   .Build();
            }
            else if (startTime != null && endTime == null)
            {
                trigger = TriggerBuilder.Create()
                   .WithIdentity(triggerName ?? jobName, triggerGroupName)
                   .StartAt(CronHelper.DateTime2DateTimeOffset(startTime.Value))
                   .WithCronSchedule(cronExpression)
                   .Build();
            }
            else if (startTime == null && endTime != null)
            {
                trigger = TriggerBuilder.Create()
                   .WithIdentity(triggerName ?? jobName, triggerGroupName)
                   .EndAt(CronHelper.DateTime2DateTimeOffset(endTime.Value))
                   .WithCronSchedule(cronExpression)
                   .Build();
            }
            else
            {
                trigger = TriggerBuilder.Create()
                  .WithIdentity(triggerName ?? jobName, triggerGroupName)
                  .StartAt(CronHelper.DateTime2DateTimeOffset(startTime.Value))
                  .EndAt(CronHelper.DateTime2DateTimeOffset(endTime.Value))
                  .WithCronSchedule(cronExpression)
                  .Build();
            }


            await _scheduler.ScheduleJob(job, trigger);

            await _scheduler.Start();

            return await _scheduler.GetTriggerState(trigger.Key);
        }

        public async Task<TriggerState> ModifyJobTime(string cronExpression, string jobName, string jobGroupName = JOB_GROUP_NAME, string triggerName = null, string triggerGroupName = TRIGGER_GROUP_NAME)
        {
            IJobDetail job;
            ITrigger trigger;

            job = await _scheduler.GetJobDetail(new JobKey(jobName, jobGroupName));
            trigger = await _scheduler.GetTrigger(new TriggerKey(triggerName ?? jobName, triggerGroupName));
            if (job == null || trigger == null)
            {
                return TriggerState.None;
            }

            await _scheduler.UnscheduleJob(trigger.Key);

            trigger = TriggerBuilder.Create()
             .WithIdentity(trigger.Key.Name, trigger.Key.Group)
            .WithCronSchedule(cronExpression)
            .Build();

            await _scheduler.ScheduleJob(job, trigger);

            return await _scheduler.GetTriggerState(trigger.Key);
        }

        public async Task<TriggerState> RemoveJob(string jobName, string jobGroupName = JOB_GROUP_NAME, string triggerName = null, string triggerGroupName = TRIGGER_GROUP_NAME)
        {

            IJobDetail job;
            ITrigger trigger;

            job = await _scheduler.GetJobDetail(new JobKey(jobName, jobGroupName));
            trigger = await _scheduler.GetTrigger(new TriggerKey(triggerName ?? jobName, triggerGroupName));
            if (job == null || trigger == null)
            {
                return TriggerState.None;
            }

            await _scheduler.UnscheduleJob(trigger.Key);
            await _scheduler.DeleteJob(job.Key);

            return await _scheduler.GetTriggerState(trigger.Key);
        }

        public async Task<TriggerState> PauseJob(string jobName, string jobGroupName = JOB_GROUP_NAME, string triggerName = null, string triggerGroupName = TRIGGER_GROUP_NAME)
        {
            IJobDetail job;
            ITrigger trigger;

            job = await _scheduler.GetJobDetail(new JobKey(jobName, jobGroupName));
            trigger = await _scheduler.GetTrigger(new TriggerKey(triggerName ?? jobName, triggerGroupName));
            if (job == null || trigger == null)
            {
                return TriggerState.None;
            }

            await _scheduler.PauseJob(job.Key);

            return await _scheduler.GetTriggerState(trigger.Key);
        }

        public async Task<TriggerState> ResumeJob(string jobName, string jobGroupName = JOB_GROUP_NAME, string triggerName = null, string triggerGroupName = TRIGGER_GROUP_NAME)
        {
            IJobDetail job;
            ITrigger trigger;

            job = await _scheduler.GetJobDetail(new JobKey(jobName, jobGroupName));
            trigger = await _scheduler.GetTrigger(new TriggerKey(triggerName ?? jobName, triggerGroupName));
            if (job == null || trigger == null)
            {
                return TriggerState.None;
            }

            await _scheduler.ResumeJob(job.Key);

            return await _scheduler.GetTriggerState(trigger.Key);
        }

        public async Task<TriggerState> GetJobState(string jobName, string jobGroupName = JOB_GROUP_NAME, string triggerName = null, string triggerGroupName = TRIGGER_GROUP_NAME)
        {
            IJobDetail job;
            ITrigger trigger;

            job = await _scheduler.GetJobDetail(new JobKey(jobName, jobGroupName));
            trigger = await _scheduler.GetTrigger(new TriggerKey(triggerName ?? jobName, triggerGroupName));
            if (job == null || trigger == null)
            {
                return TriggerState.None;
            }
            return await _scheduler.GetTriggerState(trigger.Key);
        }

        public async Task InitJobs()
        {
            if (_scheduler != null)
            {
                return;
            }

            //配置文件的方式，配置quartz实例
            ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
            _scheduler = await schedulerFactory.GetScheduler();
        }

        public async Task StartJobs()
        {
            if (_scheduler != null)
            {
                await _scheduler.Start(); return;
            }

            //配置文件的方式，配置quartz实例
            ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
            _scheduler = await schedulerFactory.GetScheduler();
            await _scheduler.Start();
        }

        public void StandbyJobs()
        {
            try
            {
                if (_scheduler == null) return;
                if (!_scheduler.InStandbyMode)
                {
                    _scheduler.Standby();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void RestartJobs()
        {
            _scheduler.Shutdown();
            _scheduler.Start();
        }

        public DateTime[] GetNextRunTime(string cronExpression, DateTime? startTime, DateTime? endTime, int number = 1)
        {
            try
            {
                ITrigger trigger = null;
                if (startTime == null && endTime == null)
                {
                    trigger = TriggerBuilder.Create()
                        .WithCronSchedule(cronExpression)
                        .Build();
                }
                else if (startTime != null && endTime == null)
                {
                    trigger = TriggerBuilder.Create()
                       .StartAt(CronHelper.DateTime2DateTimeOffset(startTime.Value))
                       .WithCronSchedule(cronExpression)
                       .Build();
                }
                else if (startTime == null && endTime != null)
                {
                    trigger = TriggerBuilder.Create()
                       .EndAt(CronHelper.DateTime2DateTimeOffset(endTime.Value))
                       .WithCronSchedule(cronExpression)
                       .Build();
                }
                else
                {
                    trigger = TriggerBuilder.Create()
                      .StartAt(CronHelper.DateTime2DateTimeOffset(startTime.Value))
                      .EndAt(CronHelper.DateTime2DateTimeOffset(endTime.Value))
                      .WithCronSchedule(cronExpression)
                      .Build();
                }


                IList<DateTimeOffset> dates = TriggerUtils.ComputeFireTimes(trigger as IOperableTrigger, null, number).ToList();
                if (dates != null && dates.Count > 0)
                {
                    return dates.Select(p => TimeZoneInfo.ConvertTimeFromUtc(p.DateTime, TimeZoneInfo.Local)).ToArray();
                }
            }
            catch (Exception ex)
            {

            }
            return null;
        }
    }
}
